package effdom.benchmark.xml;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.effdom.Document;
import org.effdom.DocumentFactory;
import org.effdom.Element;
import org.effdom.DocumentFactory.CompressType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import effdom.benchmark.BenchmarkResult;

public class XMLTester {

    private String fileName;

    private Map<String, Short> idMap = new HashMap<String, Short>();

    private short index = 0;

    public XMLTester(String file) {
        this.fileName = file;
    }

    private void generateReport() throws SAXException, IOException, ParserConfigurationException, TransformerFactoryConfigurationError,
            TransformerException {
        BenchmarkResult result = new BenchmarkResult("xml");
        result.setFileSize((int) new File(fileName).length());
        long start = System.currentTimeMillis();
        org.w3c.dom.Document xmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(fileName);
        long stop = System.currentTimeMillis();
        result.setReadTime(stop - start);
        start = System.currentTimeMillis();
        TransformerFactory.newInstance().newTransformer().transform(new DOMSource(xmlDoc), new StreamResult(fileName + "_out2.xml"));
        stop = System.currentTimeMillis();
        result.setWriteTime(stop - start);
        Document doc = testEffDom(xmlDoc);
        addDict(doc);
        System.out.println(doc);
        System.out.println(result);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DocumentFactory.createInstance().createDocumentWriter(CompressType.NONE).write(doc, out);
        System.out.println(out.toByteArray().length);
    }

    private void addDict(Document doc) {
        Element dict = doc.rootElement().element((short)-1);
        for(String name : idMap.keySet()) {
            dict.attr(idMap.get(name), name);
        }
    }

    private short getID(String id) {
        if (!idMap.containsKey(id)) {
            idMap.put(id, ++index);
        }
        return idMap.get(id);
    }

    private Document testEffDom(org.w3c.dom.Document xmlDoc) {
        Document doc = DocumentFactory.createInstance().createDocument();
        org.w3c.dom.Element xmlRoot = xmlDoc.getDocumentElement();
        Element root = doc.rootElement(getID(xmlRoot.getNodeName()));
        setAttr(xmlRoot, root);
        setElement(xmlRoot, root);
        return doc;
    }

    private void setElement(org.w3c.dom.Element xmlElem, Element elem) {
        NodeList list = xmlElem.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            if (list.item(i) instanceof org.w3c.dom.Element) {
                org.w3c.dom.Element xmlChild = (org.w3c.dom.Element) list.item(i);
                Element child = elem.element(getID(xmlChild.getNodeName()));
                setAttr(xmlChild, child);
                setElement(xmlChild, child);
            } else if (list.item(i) instanceof org.w3c.dom.Text) {
                String text = ((org.w3c.dom.Text) list.item(i)).getData().trim();
                if (text.length() > 0) {
                    elem.attr((short)-1, text);
                }
            } else {
                throw new RuntimeException("Not valid format: " + list.item(i).getClass().getName());
            }
        }
    }

    private void setAttr(org.w3c.dom.Element xmlElem, Element elem) {
        NamedNodeMap map = xmlElem.getAttributes();
        for (int i = 0; i < map.getLength(); i++) {
            org.w3c.dom.Attr xmlAttr = (org.w3c.dom.Attr) map.item(i);
            elem.attr(getID(xmlAttr.getNodeName()), xmlAttr.getValue());
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        if (args != null && args.length > 0 && args[0].endsWith("xml")) {
            try {
                new XMLTester(args[0]).generateReport();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else {
            System.out.println("Suppy with XML file");
        }

    }
}
